package org.unidata.mdm.dq.core.context;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.unidata.mdm.core.context.AbstractModelChangeContext;
import org.unidata.mdm.core.service.segments.ModelUpsertStartExecutor;
import org.unidata.mdm.dq.core.configuration.DataQualityModelIds;
import org.unidata.mdm.dq.core.type.model.source.AbstractCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.CleanseFunctionGroup;
import org.unidata.mdm.dq.core.type.model.source.CompositeCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.GroovyCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.JavaCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.PythonCleanseFunctionSource;
import org.unidata.mdm.dq.core.type.model.source.assignment.NameSpaceAssignmentSource;
import org.unidata.mdm.dq.core.type.model.source.rule.MappingSetSource;
import org.unidata.mdm.dq.core.type.model.source.rule.QualityRuleSource;
import org.unidata.mdm.dq.core.util.DQUtils;
import org.unidata.mdm.draft.context.DraftDataContext;
import org.unidata.mdm.system.context.DraftAwareContext;
import org.unidata.mdm.system.type.pipeline.PipelineInput;

/**
 * @author Mikhail Mikhailov on Oct 8, 2020
 */
public class UpsertQualityModelContext
    extends AbstractModelChangeContext
    implements DraftAwareContext, DraftDataContext, PipelineInput {
    /**
     * GSVUID.
     */
    private static final long serialVersionUID = 7169865700344716584L;
    /**
     * Enumerations updates.
     */
    private final CleanseFunctionGroup groupsUpdate;
    /**
     * Functions update.
     */
    private final List<JavaCleanseFunctionSource> javaFunctionsUpdate = new ArrayList<>();
    /**
     * Functions update.
     */
    private final List<CompositeCleanseFunctionSource> compositeFunctionsUpdate = new ArrayList<>();
    /**
     * Functions update.
     */
    private final List<GroovyCleanseFunctionSource> groovyFunctionsUpdate = new ArrayList<>();
    /**
     * Functions update.
     */
    private final List<PythonCleanseFunctionSource> pythonFunctionsUpdate = new ArrayList<>();
    /**
     * Functions deletes.
     */
    private final List<String> functionsDelete;
    /**
     * Rules update.
     */
    private final List<QualityRuleSource> rulesUpdate;
    /**
     * Rules deletes.
     */
    private final List<String> rulesDelete;
    /**
     * Rule sets update.
     */
    private final List<MappingSetSource> setsUpdate;
    /**
     * Rule sets deletes.
     */
    private final List<String> setsDelete;
    /**
     * Assignments update.
     */
    private final List<NameSpaceAssignmentSource> assignmentsUpdate;
    /**
     * Assignments deletes.
     */
    private final List<String> assignmentsDelete;
    /**
     * A possibly set draft id.
     */
    private final Long draftId;
    /**
     * A possibly set parent draft id.
     */
    private final Long parentDraftId;
    /**
     * Constructor.
     */
    private UpsertQualityModelContext(UpsertQualityModelContextBuilder b) {
        super(b);
        this.groupsUpdate = b.groupsUpdate;
        this.functionsDelete = Objects.isNull(b.functionsDelete) ? Collections.emptyList() : b.functionsDelete;

        if (CollectionUtils.isNotEmpty(b.functionsUpdate)) {

            b.functionsUpdate.forEach(f -> {
                switch (f.getType()) {
                case COMPOSITE:
                    this.compositeFunctionsUpdate.add((CompositeCleanseFunctionSource) f);
                    break;
                case GROOVY:
                    this.groovyFunctionsUpdate.add((GroovyCleanseFunctionSource) f);
                    break;
                case JAVA:
                    this.javaFunctionsUpdate.add((JavaCleanseFunctionSource) f);
                    break;
                case PYTHON:
                    this.pythonFunctionsUpdate.add((PythonCleanseFunctionSource) f);
                    break;
                default:
                    break;
                }
            });
        }

        this.rulesUpdate = Objects.isNull(b.rulesUpdate) ? Collections.emptyList() : b.rulesUpdate;
        this.rulesDelete = Objects.isNull(b.rulesDelete) ? Collections.emptyList() : b.rulesDelete;
        this.setsUpdate = Objects.isNull(b.setsUpdate) ? Collections.emptyList() : b.setsUpdate;
        this.setsDelete = Objects.isNull(b.setsDelete) ? Collections.emptyList() : b.setsDelete;
        this.assignmentsUpdate = Objects.isNull(b.assignmentsUpdate) ? Collections.emptyList() : b.assignmentsUpdate;
        this.assignmentsDelete = Objects.isNull(b.assignmentsDelete) ? Collections.emptyList() : b.assignmentsDelete;
        this.draftId = b.draftId;
        this.parentDraftId = b.parentDraftId;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getTypeId() {
        return DataQualityModelIds.DATA_QUALITY;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getInstanceId() {
        // DQ model is singletons per storage
        return DQUtils.DEFAULT_MODEL_INSTANCE_ID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getStartTypeId() {
        return ModelUpsertStartExecutor.SEGMENT_ID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Long getDraftId() {
        return draftId;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Long getParentDraftId() {
        return parentDraftId;
    }
    /**
     * @return the enumerationsUpdate
     */
    public CleanseFunctionGroup getGroupsUpdate() {
        return groupsUpdate;
    }
    /**
     * @return the functionsUpdate
     */
    public List<JavaCleanseFunctionSource> getJavaFunctionsUpdate() {
        return javaFunctionsUpdate;
    }
    /**
     * @return the functionsUpdate
     */
    public List<CompositeCleanseFunctionSource> getCompositeFunctionsUpdate() {
        return compositeFunctionsUpdate;
    }
    /**
     * @return the functionsUpdate
     */
    public List<GroovyCleanseFunctionSource> getGroovyFunctionsUpdate() {
        return groovyFunctionsUpdate;
    }
    /**
     * @return the functionsUpdate
     */
    public List<PythonCleanseFunctionSource> getPythonFunctionsUpdate() {
        return pythonFunctionsUpdate;
    }
    /**
     * @return the functionsDelete
     */
    public List<String> getFunctionsDelete() {
        return functionsDelete;
    }
    /**
     * @return the rulesUpdate
     */
    public List<QualityRuleSource> getRulesUpdate() {
        return rulesUpdate;
    }
    /**
     * @return the rulesDelete
     */
    public List<String> getRulesDelete() {
        return rulesDelete;
    }
    /**
     * @return the setsUpdate
     */
    public List<MappingSetSource> getSetsUpdate() {
        return setsUpdate;
    }
    /**
     * @return the setsDelete
     */
    public List<String> getSetsDelete() {
        return setsDelete;
    }
    /**
     * @return the assignmentsUpdate
     */
    public List<NameSpaceAssignmentSource> getAssignmentsUpdate() {
        return assignmentsUpdate;
    }
    /**
     * @return the assignmentsDelete
     */
    public List<String> getAssignmentsDelete() {
        return assignmentsDelete;
    }
    /**
     * Has entity update.
     *
     * @return true if so false otherwise
     */
    public boolean hasJavaFunctionsUpdate() {
        return CollectionUtils.isNotEmpty(javaFunctionsUpdate);
    }
    /**
     * Has lookup entity update.
     *
     * @return true if so false otherwise
     */
    public boolean hasCompositeFunctionsUpdate() {
        return CollectionUtils.isNotEmpty(compositeFunctionsUpdate);
    }
    /**
     * Has nested entity update.
     *
     * @return true if so false otherwise
     */
    public boolean hasPythonFunctionsUpdate() {
        return CollectionUtils.isNotEmpty(pythonFunctionsUpdate);
    }
    /**
     * Has relations update.
     *
     * @return true if so false otherwise
     */
    public boolean hasGroovyFunctionsUpdate() {
        return CollectionUtils.isNotEmpty(groovyFunctionsUpdate);
    }
    /**
     * Has group update.
     *
     * @return true, if has, false otherwise
     */
    public boolean hasFunctionGroupUpdate() {
        return groupsUpdate != null;
    }
    /**
     * Has entity delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasFunctionsDelete() {
        return CollectionUtils.isNotEmpty(functionsDelete);
    }
    /**
     * Has entity delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasRulesUpdate() {
        return CollectionUtils.isNotEmpty(rulesUpdate);
    }
    /**
     * Has entity delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasRulesDelete() {
        return CollectionUtils.isNotEmpty(rulesDelete);
    }
    /**
     * Has entity delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasSetsUpdate() {
        return CollectionUtils.isNotEmpty(setsUpdate);
    }
    /**
     * Has entity delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasSetsDelete() {
        return CollectionUtils.isNotEmpty(setsDelete);
    }
    /**
     * Has assignments update.
     *
     * @return true if so false otherwise
     */
    public boolean hasAssignmentsUpdate() {
        return CollectionUtils.isNotEmpty(assignmentsUpdate);
    }
    /**
     * Has assignments delete.
     *
     * @return true if so false otherwise
     */
    public boolean hasAssignmentsDelete() {
        return CollectionUtils.isNotEmpty(assignmentsDelete);
    }
    /**
     * Builder instance.
     * @return builder instance
     */
    public static UpsertQualityModelContextBuilder builder() {
        return new UpsertQualityModelContextBuilder();
    }
    /**
     * @author Mikhail Mikhailov on Oct 9, 2020
     */
    public static class UpsertQualityModelContextBuilder extends AbstractModelChangeContextBuilder<UpsertQualityModelContextBuilder> {
        /**
         * Groups updates.
         */
        private CleanseFunctionGroup groupsUpdate;
        /**
         * Functions update.
         */
        private List<AbstractCleanseFunctionSource<?>> functionsUpdate;
        /**
         * Functions deletes.
         */
        private List<String> functionsDelete;
        /**
         * Rules update.
         */
        private List<QualityRuleSource> rulesUpdate;
        /**
         * Rules deletes.
         */
        private List<String> rulesDelete;
        /**
         * Rule sets update.
         */
        private List<MappingSetSource> setsUpdate;
        /**
         * Rule sets deletes.
         */
        private List<String> setsDelete;
        /**
         * Assignments update.
         */
        private List<NameSpaceAssignmentSource> assignmentsUpdate;
        /**
         * Assignments deletes.
         */
        private List<String> assignmentsDelete;
        /**
         * The draft id.
         */
        private Long draftId;
        /**
         * The parent draft id.
         */
        private Long parentDraftId;
        /**
         * Constructor.
         */
        private UpsertQualityModelContextBuilder() {
            super();
        }
        /**
         * Sets draft id
         * @param draftId the draft id
         * @return self
         */
        public UpsertQualityModelContextBuilder draftId(Long draftId) {
            this.draftId = draftId;
            return self();
        }
        /**
         * Sets parent draft id
         * @param parentDraftId the parent draft id
         * @return self
         */
        public UpsertQualityModelContextBuilder parentDraftId(Long parentDraftId) {
            this.parentDraftId = parentDraftId;
            return self();
        }

        public UpsertQualityModelContextBuilder groupsUpdate(CleanseFunctionGroup ss) {
            this.groupsUpdate = ss;
            return self();
        }

        public UpsertQualityModelContextBuilder functionsUpdate(AbstractCleanseFunctionSource<?>... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return functionsUpdate(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder functionsUpdate(Collection<AbstractCleanseFunctionSource<?>> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (functionsUpdate == null) {
                    functionsUpdate = new ArrayList<>();
                }

                functionsUpdate.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder rulesUpdate(QualityRuleSource... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return rulesUpdate(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder rulesUpdate(Collection<QualityRuleSource> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (rulesUpdate == null) {
                    rulesUpdate = new ArrayList<>();
                }

                rulesUpdate.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder setsUpdate(MappingSetSource... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return setsUpdate(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder setsUpdate(Collection<MappingSetSource> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (setsUpdate == null) {
                    setsUpdate = new ArrayList<>();
                }

                setsUpdate.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder assignmentsUpdate(NameSpaceAssignmentSource... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return assignmentsUpdate(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder assignmentsUpdate(Collection<NameSpaceAssignmentSource> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (assignmentsUpdate == null) {
                    assignmentsUpdate = new ArrayList<>();
                }

                assignmentsUpdate.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder functionsDelete(String... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return functionsDelete(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder functionsDelete(Collection<String> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (functionsDelete == null) {
                    functionsDelete = new ArrayList<>();
                }

                functionsDelete.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder rulesDelete(String... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return rulesDelete(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder rulesDelete(Collection<String> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (rulesDelete == null) {
                    rulesDelete = new ArrayList<>();
                }

                rulesDelete.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder setsDelete(String... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return setsDelete(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder setsDelete(Collection<String> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (setsDelete == null) {
                    setsDelete = new ArrayList<>();
                }

                setsDelete.addAll(ss);
            }
            return self();
        }

        public UpsertQualityModelContextBuilder assignmentsDelete(String... ss) {
            if (ArrayUtils.isNotEmpty(ss)) {
                return assignmentsDelete(Arrays.asList(ss));
            }
            return self();
        }

        public UpsertQualityModelContextBuilder assignmentsDelete(Collection<String> ss) {
            if (CollectionUtils.isNotEmpty(ss)) {
                if (assignmentsDelete == null) {
                    assignmentsDelete = new ArrayList<>();
                }

                assignmentsDelete.addAll(ss);
            }
            return self();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public UpsertQualityModelContext build() {
            return new UpsertQualityModelContext(this);
        }
    }
}
